<?php

namespace PhpYes\Modules\Cmd;


abstract class CmdBase extends CmdArgumentDefinitions
{
    /** @var string */
    private $alias;
    /** @var string */
    private $indentUnit;
    /** @var string */
    private $hangIndent;
    /** @var string */
    private $lineSeparator;

    /**
     * CmdBase constructor.
     * @param string|null $alias
     */
    public function __construct($alias = null)
    {
        $this->alias = $alias;
        $this->indentUnit = '    ';
        $this->hangIndent = '    ';
        $this->lineSeparator = PHP_EOL;

        parent::__construct();
    }

    /**
     * @return string
     */
    public function getAlias()
    {
        if (empty($this->alias)) {
            return $this->getName();
        }

        return $this->alias;
    }

    /**
     * @return string
     */
    abstract public function getName();

    /**
     * @return string
     */
    abstract public function getDescription();

    /**
     * @return string
     */
    abstract public function getDetailDescription();

    /**
     * 命令的帮助文档
     * NAME
     * SYNOPSIS
     * DESCRIPTION
     * OPTIONS
     * EXAMPLES
     * DISCUSSION
     * ...
     * @return string
     */
    abstract public function help();

    /**
     * 执行命令
     * @param int $argc
     * @param array $argv
     * @throws \Exception
     */
    abstract public function execute($argc, $argv);

    /**
     * @return string
     */
    public function getIndentUnit()
    {
        return $this->indentUnit;
    }

    /**
     * @param string $indentUnit
     */
    public function setIndentUnit($indentUnit)
    {
        $this->indentUnit = $indentUnit;
    }

    /**
     * @return string
     */
    public function getHangIndent()
    {
        return $this->hangIndent;
    }

    /**
     * @param string $hangIndent
     */
    public function setHangIndent($hangIndent)
    {
        $this->hangIndent = $hangIndent;
    }

    /**
     * @return string
     */
    public function getLineSeparator()
    {
        return $this->lineSeparator;
    }

    /**
     * @param string $lineSeparator
     */
    public function setLineSeparator($lineSeparator)
    {
        $this->lineSeparator = $lineSeparator;
    }

    /**
     * @return string
     */
    public function getUsage()
    {
        if ($this instanceof SubCmdBase) {
            $usage = $this->getHangIndent() . $this->getParentCmd()->getName() . ' ' . $this->getAlias();
        } else {
            $usage = $this->getHangIndent() . $this->getName();
        }

        $argDefs = $this->getArgumentDefinitions();
        if ($argDefs) {
            foreach ($argDefs as $argDef) {
                $argName = $argDef->getName();
                $short = $argDef->getShort();
                $long = $argDef->getLong();
                if (empty($argName)) {
                    continue;
                }

                if ($short && $long) {
                    if ($argDef->isRequire()) {
                        if ($argDef->isRequireValue()) {
                            $argDoc = "<{$short} <{$argName}> | {$long}=<{$argName}>>";
                        } else {
                            $argDoc = "<{$short} | {$long}>";
                        }
                    } else {
                        if ($argDef->isRequireValue()) {
                            $argDoc = "[{$short} <{$argName}> | {$long}=<{$argName}>]";
                        } else {
                            $argDoc = "[{$short} | {$long}]";
                        }
                    }
                } elseif ($short) {
                    if ($argDef->isRequire()) {
                        if ($argDef->isRequireValue()) {
                            $argDoc = "{$short} <{$argName}>";
                        } else {
                            $argDoc = "{$short}";
                        }
                    } else {
                        if ($argDef->isRequireValue()) {
                            $argDoc = "[{$short} <{$argName}>]";
                        } else {
                            $argDoc = "[{$short}]";
                        }
                    }
                } elseif ($long) {
                    if ($argDef->isRequire()) {
                        if ($argDef->isRequireValue()) {
                            $argDoc = "{$long}=<{$argName}>";
                        } else {
                            $argDoc = "{$long}";
                        }
                    } else {
                        if ($argDef->isRequireValue()) {
                            $argDoc = "[{$long}=<{$argName}>]";
                        } else {
                            $argDoc = "[{$long}]";
                        }
                    }
                } else {
                    if ($argDef->isRequire()) {
                        $argDoc = "<{$argName}>";
                    } else {
                        $argDoc = "[<{$argName}>]";
                    }
                }

                $usage .= " {$argDoc}";
            }
        }

        return $usage;
    }

    /**
     * @return string
     */
    public function getOptionDescription()
    {
        $desc = '';
        $indent = $this->getIndentUnit();
        $hang = $this->getHangIndent();
        $eol = $this->getLineSeparator();

        $argDefs = $this->getArgumentDefinitions();
        if ($argDefs) {
            $first = true;
            foreach ($argDefs as $argDef) {
                $argName = $argDef->getName();
                $short = $argDef->getShort();
                $long = $argDef->getLong();
                if (empty($argName)) {
                    continue;
                }
                if (empty($short) || empty($long)) {
                    continue;
                }

                if ($first) {
                    $first = false;
                } else {
                    $desc .= "{$eol}{$eol}";
                }

                if ($short && $long) {
                    if ($argDef->isRequireValue()) {
                        $desc .= $hang . "{$short} <$argName>, {$long}=<{$argName}>";
                    } else {
                        $desc .= $hang . "{$short}, {$long}";
                    }
                } elseif ($short) {
                    if ($argDef->isRequireValue()) {
                        $desc .= $hang . "{$short} <$argName>";
                    } else {
                        $desc .= $hang . "{$short}";
                    }
                } elseif ($long) {
                    if ($argDef->isRequireValue()) {
                        $desc .= $hang . "{$long}=<{$argName}>";
                    } else {
                        $desc .= $hang . "{$long}";
                    }
                }

                $argDesc = $argDef->getDescription();
                if ($argDesc) {
                    $desc .= "{$eol}{$hang}{$indent}{$argDesc}";
                }
            }
        }

        return $desc;
    }

}